In order to access a service, a consumer program must
import the service contract and other dependencies. WCF services
typically expose their contract using an address of the service endpoint
or its MEX endpoint.
If the service consumer is
implemented within WCF, it uses a proxy class that is a CLR type to
access the service contract. The service may have more than one
endpoint, in which case the service consumer must have a proxy for each
endpoint of the service. The proxy class encapsulates every aspect of
the service, including its operations, address, and binding. It also
includes methods to manage the lifecycle of the proxy and its connection
to the service.
Visual Studio has tools for
importing service metadata, generating the proxy class, and integrating
the generated proxy class into a Visual Studio solution. This can be
done using the Add Service reference feature in the solution explorer.
Under the hood, Visual Studio
uses the Service Metadata Tool (svcutil.exe). Besides supporting Visual
Studio, this tool provides several options through switches that can be
used to generate proxies for different scenarios.
There are three ways a service consumer implemented in WCF can consume a service:
1. | Using the Service Metadata Tool
|
2. | Writing the Proxy Class for a Service
|
3. | Using the ChannelFactory Class
|
Let’s explore each individually.
Using the Service Metadata Tool
The Service Metadata Tool,
which can be accessed from the command prompt, is used to create the
client proxy and the client configuration file. To use this tool, the
service consumer needs to know where the service is located and how to
connect to it. Therefore, the client configuration file must contain the
same endpoint information as the service to which it is connecting. The
address and binding data can be found in the service provider’s
endpoint and can be accessed using an MEX endpoint or the actual
endpoint address.
The Service Metadata Tool
is given the endpoint address and the names of the proxy and
configuration files to output. In the following example, the service
endpoint is located at “http://localhost:8000/Accounts” and the Service
Metadata Tool will generate two files: proxy.cs and App.Config. The
proxy.cs file will contain the generated proxy class and App.Config will
contain endpoint information to support the proxy class.
Example 1.
svcutil http://localhost:8000/Accounts/out:proxy.cs /Config:app.config
|
The next step is to include the
generated files into the development project. The generated class, which
represents the contract, can now be accessed by instantiating the proxy
class:
Example 2.
AccountProxy proxy = new AccountProxy("EndPoint1"); string response = proxy.GetHoldings(); Console.WriteLine(response); proxy.Close();
|
In this code fragment, proxy is the typed instance of the contract and EndPoint1 identifies an endpoint in the client application’s configuration file.
Writing the Proxy Class for a Service
The proxy class generated by the Service Metadata Tool uses the generic ClientBase<T> class, which is defined as:
Example 5.48.
public class ClientBase<T> :IDisposable { protected ClientBase (string endpointConfigurationName); protected ClientBase( Binding binding, EndpointAddress remoteAddress); public void Close(); public void Dispose(); protected T InnerProxy{get;} }
|
Note that the ClientBase<T> class can also be used to create a proxy class by writing code to access a service.
The service consumer needs to instantiate a ClientBase<T>
object and provide the constructor with either the endpoint defined in
the configuration file or the address and binding objects. InnerProxy method then returns a typed instance of the contract.
The following example demonstrates the creation of a proxy class for a service:
Example 3.
[ServiceContract] public interface IGreetings { [OperationContract] string Greet(); }
|
A typed proxy for the service contract is created using this code:
Example 5.50.
public partial class GreetingsProxy : ClientBase<IGreetings>, IGreetings { public GreetingsProxy(string configurationName) : base(configurationName){} public string Greet() { return Channel.Greet(); } }
|
The client instantiating the GreetingsProxy must give it the endpoint by either referring to the configuration file or by providing the address and binding objects.
Using the ChannelFactory Class
WCF provides channels that allow
an operation in a service to be invoked directly without using the
Service Metadata Tool. It is mostly suitable in a closed development
environment, where the client has direct access to the endpoint
information.
The ChannelFactory<T> class in System.ServiceModel is used to create a proxy at runtime. The ChannelFactory<T> constructor is given either the endpoint name in the application’s configuration file or the endpoint object. CreateChannel is a typed method that returns the IGreet proxy. The typed proxy class can be used to access all operations exposed by the service contract:
Example 4.
ChannelFactory factory = new ChannelFactory<IGreet>("Endpoint1"); IGreet proxy = factory.CreateChannel(); string greeting = proxy.Greet("Hello World"); Console.WriteLine(greeting);
|
The configuration file contains the endpoint information used in the ChannelFactory constructor:
Example 5.
<system.serviceModel> <client> <endpoint configurationName="Endpoint1" address="http://localhost/GreetingsService" binding="basicHttpBinding" contract="IGreet" /> </client> </system.serviceModel>
|
Regenerating the proxy class
each time a service changes can be inefficient. However, creating a
proxy on the fly can result in performance degradation. Considerations
such as this need to be taken into account before deciding on the most
appropriate approach.